package com.gitee.wsl.mathematics.fuction.frequency

import com.gitee.wsl.mathematics.function.frequency.FrequencySketch
import java.util.concurrent.ThreadLocalRandom
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertNull
import kotlin.test.assertTrue


/**
 * @author ben.manes@gmail.com (Ben Manes)
 */
class FrequencySketchTest {
    val item: Int = ThreadLocalRandom.current().nextInt()

    @Test
    fun construct() {
        val sketch = FrequencySketch<Int>()
        assertNull(sketch.table)
        assertTrue(sketch.isNotInitialized)

        sketch.increment(item)
        assertEquals(sketch.frequency(item),0)
    }

    /*@Test()
    fun ensureCapacity_negative(sketch: FrequencySketch<Int?>) {
        assertThrows(IllegalArgumentException::class.java) { sketch.ensureCapacity(-1) }
    }

    @Test()
    fun ensureCapacity_smaller(sketch: FrequencySketch<Int?>) {
        val size = sketch.table!!.size
        sketch.ensureCapacity((size / 2).toLong())
        assertThat(sketch.table).hasLength(size)
        assertThat(sketch.sampleSize).isEqualTo(10 * size)
        assertThat(sketch.blockMask).isEqualTo((size shr 3) - 1)
    }

    @Test(dataProvider = "sketch")
    fun ensureCapacity_larger(sketch: FrequencySketch<Int?>) {
        val size = sketch.table!!.size
        sketch.ensureCapacity(2L * size)
        assertThat(sketch.table).hasLength(2 * size)
        assertThat(sketch.sampleSize).isEqualTo(10 * 2 * size)
        assertThat(sketch.blockMask).isEqualTo(((2 * size) shr 3) - 1)
    }

    @Test(dataProvider = "sketch", groups = "isolated")
    fun ensureCapacity_maximum(sketch: FrequencySketch<Int?>) {
        val size = Int.MAX_VALUE / 10 + 1
        sketch.ensureCapacity(size.toLong())
        assertThat(sketch.sampleSize).isEqualTo(Int.MAX_VALUE)
        assertThat(sketch.table).hasLength(ceilingPowerOfTwo(size))
        assertThat(sketch.blockMask).isEqualTo((sketch.table!!.size shr 3) - 1)
    }

    @Test()
    fun increment_once(sketch: FrequencySketch<Int?>) {
        sketch.increment(item)
        assertThat(sketch.frequency(item)).isEqualTo(1)
    }

    @Test()
    fun increment_max(sketch: FrequencySketch<Int?>) {
        for (i in 0..19) {
            sketch.increment(item)
        }
        assertThat(sketch.frequency(item)).isEqualTo(15)
    }

    @Test()
    fun increment_distinct(sketch: FrequencySketch<Int?>) {
        sketch.increment(item)
        sketch.increment(item + 1)
        assertThat(sketch.frequency(item)).isEqualTo(1)
        assertThat(sketch.frequency(item + 1)).isEqualTo(1)
        assertThat(sketch.frequency(item + 2)).isEqualTo(0)
    }

    @Test()
    fun increment_zero(sketch: FrequencySketch<Int?>) {
        sketch.increment(0)
        assertThat(sketch.frequency(0)).isEqualTo(1)
    }

    @Test
    fun reset() {
        var reset = false
        val sketch = FrequencySketch<Int>()
        sketch.ensureCapacity(64)

        for (i in 1..<20 * sketch.table!!.size) {
            sketch.increment(i)
            if (sketch.size != i) {
                reset = true
                break
            }
        }
        assertThat(reset).isTrue()
        assertThat(sketch.size).isAtMost(sketch.sampleSize / 2)
    }

    @Test
    fun full() {
        val sketch = makeSketch<Int>(512)
        sketch.sampleSize = Int.MAX_VALUE
        for (i in 0..100000 - 1) {
            sketch.increment(i)
        }
        for (slot in sketch.table!!) {
            assertThat(java.lang.Long.bitCount(slot)).isEqualTo(64)
        }

        sketch.reset()
        for (slot in sketch.table!!) {
            assertThat(slot).isEqualTo(FrequencySketch.RESET_MASK)
        }
    }

    @Test
    fun heavyHitters() {
        val sketch = makeSketch<Double>(512)
        for (i in 100..<100000) {
            sketch.increment(i.toDouble())
        }
        run {
            var i = 0
            while (i < 10) {
                for (j in 0..<i) {
                    sketch.increment(i.toDouble())
                }
                i += 2
            }
        }

        // A perfect popularity count yields an array [0, 0, 2, 0, 4, 0, 6, 0, 8, 0]
        val popularity = IntArray(10)
        for (i in 0..9) {
            popularity[i] = sketch.frequency(i.toDouble())
        }
        for (i in popularity.indices) {
            if ((i == 0) || (i == 1) || (i == 3) || (i == 5) || (i == 7) || (i == 9)) {
                assertThat(popularity[i]).isAtMost(popularity[2])
            } else if (i == 2) {
                assertThat(popularity[2]).isAtMost(popularity[4])
            } else if (i == 4) {
                assertThat(popularity[4]).isAtMost(popularity[6])
            } else if (i == 6) {
                assertThat(popularity[6]).isAtMost(popularity[8])
            }
        }
    }*/


    fun providesSketch(): Array<Array<Any>> {
        return arrayOf(arrayOf(makeSketch<Any>(512)))
    }

    companion object {
        private fun <E> makeSketch(maximumSize: Long): FrequencySketch<E> {
            val sketch = FrequencySketch<E>()
            sketch.ensureCapacity(maximumSize)
            return sketch
        }
    }
}
